package com.tobdev.qywxinner.intercepter;

import com.alibaba.fastjson.JSON;
import com.tobdev.qywxinner.utils.JWTUtils;
import com.tobdev.qywxinner.utils.JsonData;
import io.jsonwebtoken.Claims;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

@Slf4j
public class TokenFilter extends OncePerRequestFilter {
    // 使用构造器注入（线程安全）
    private final JWTUtils jwtUtils;

    public TokenFilter(JWTUtils jwtUtils) {
        this.jwtUtils = jwtUtils;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {

        try {
            // 2. 提取并验证Token
            String token = extractToken(request);
            Claims claims = validateToken(token);

            // 3. 设置用户上下文
            setupUserContext(claims);

            // 4. 放行请求
            chain.doFilter(request, response);

        } catch (AuthException e) {
            // 认证失败处理
            handleAuthError(request, response, e);
        } catch (Exception e) {
            // 系统异常处理
            handleSystemError(response, e);
        } finally {
            // 确保清除上下文
            TokenContext.clear();
        }
    }

    private String extractToken(HttpServletRequest request) throws AuthException {
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            throw new AuthException(401, "Missing or invalid Authorization header");
        }
        return authHeader.substring(7).trim();
    }

    private Claims validateToken(String token) throws AuthException {
        if (token.isEmpty()) {
            throw new AuthException(401, "Empty token");
        }

        Claims claims = jwtUtils.checkJWT(token);
        if (claims == null) {
            throw new AuthException(401, "Invalid token signature");
        }

        if (claims.getExpiration().before(new Date())) {
            throw new AuthException(401, "Token expired");
        }

        return claims;
    }

    private void setupUserContext(Claims claims) throws AuthException {
        Integer userId = claims.get("uid", Integer.class);
        if (userId == null) {
            throw new AuthException(403, "Invalid user identity in token");
        }
        TokenContext.setCurrentUid(userId);
    }

    private void handleAuthError(HttpServletRequest request, HttpServletResponse response, AuthException e) throws IOException {
        log.warn("Authentication failed: {} [URI: {}]", e.getMessage(), request.getRequestURI());
        sendJsonResponse(response, e.getCode(), JsonData.buildError(e.getCode(), e.getMessage()));
    }

    private void handleSystemError(HttpServletResponse response, Exception e) throws IOException {
        log.error("System error during authentication: ", e);
        sendJsonResponse(response, 500, JsonData.buildError(500, "Internal server error"));
    }

    private void sendJsonResponse(HttpServletResponse response, int status, JsonData data) throws IOException {
        response.setStatus(status);
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        response.getWriter().write(JSON.toJSONString(data));
        response.getWriter().flush();
    }
}

@Data
class AuthException extends Exception {
    private final int code;

    public AuthException(int code, String message) {
        super(message);
        this.code = code;
    }

}